package cn.edu.cug.cs.gtl.gsm.aof;

import cn.edu.cug.cs.gtl.common.Identifier;
import cn.edu.cug.cs.gtl.common.Pair;
import cn.edu.cug.cs.gtl.gsm.PaginationStorageManager;
import cn.edu.cug.cs.gtl.gsm.item.DataItem;
import cn.edu.cug.cs.gtl.gsm.item.IndexItem;
import cn.edu.cug.cs.gtl.gsm.item.IndexMap;
import cn.edu.cug.cs.gtl.gsm.item.MetaItem;
import cn.edu.cug.cs.gtl.gsm.jdbc.postgres.DataTables;
import cn.edu.cug.cs.gtl.gsm.jdbc.postgres.IndexTable;
import cn.edu.cug.cs.gtl.gsm.jdbc.postgres.MetaTable;
import cn.edu.cug.cs.gtl.io.storage.StorageManager;
import org.apache.commons.net.ftp.FTPClient;

import java.io.*;
import java.sql.SQLException;
import java.util.*;

public abstract class AOFStorageManager extends PaginationStorageManager {

    protected IndexFile indexFile;
    protected DataFiles dataFiles;
    protected String workDirectory;

    /**
     * @param baseName
     * @param collectionSize
     * @param pageSize
     */
    public AOFStorageManager(String workDirectory, String baseName, long collectionSize, int pageSize, boolean overwrite) throws IOException{
        super(baseName, collectionSize, pageSize);
        this.workDirectory=workDirectory;
    }

    protected void initial(boolean overwrite) throws IOException {
        //确保以baseName同名的Schema必须存在
        //此时客户端开始初始化
        if(!existsDirectory(workDirectory,getBaseName())){
            createDirectory(workDirectory,getBaseName());
        }
        indexFile = createIndexFile();
        dataFiles = createDataFiles();
        if(indexFile.exists()){
            Pair<MetaItem, IndexMap> old = indexFile.read();
            if(overwrite){
                if(old!=null){
                    //删除索引文件中的数据
                    indexFile.drop();
                    indexFile.create();
                    //删除对应的数据表
                    int maxOrder = old.first().getCollectionOrder(old.first().getNextPageIdentifier());
                    for(int i=0;i<=maxOrder;++i) {
                        dataFiles.setCurrentFileOrder(i);
                        dataFiles.drop();
                    }
                }
            }
            else{
                if(old!=null){
                    //读取原来的内容
                    readMetaFromStorage();
                    readIndicesFromStorage();
                }
            }
        }
        else{//如果索引文件不存在
            indexFile.create();
        }

    }


    @Override
    protected void readBufferFromPage(Identifier pageIdentifier) throws IOException {
        dataFiles.setCurrentFileOrder(metaItem.getCollectionOrder(pageIdentifier));
        if(dataFiles.exists()){
            DataItem item = dataFiles.read(pageIdentifier);
            System.arraycopy(item.getPageContent(),0,this.pageBuffer,0,getPageSize());
        }
        else{
            throw  new IOException("DataItem "+pageIdentifier.toString()+"不存在");
        }
    }
    @Override
    protected void writeBufferToPage(Identifier pageIdentifier) throws IOException {
        dataFiles.setCurrentFileOrder(metaItem.getCollectionOrder(pageIdentifier));
        DataItem item = new DataItem(pageIdentifier, this.pageBuffer);

        if(dataFiles.exists()){
            dataFiles.write(item);
        }
        else{
            dataFiles.create();
            dataFiles.write(item);
        }

    }

    @Override
    protected void readMetaFromStorage() throws IOException {
        //合并到了索引文件中
        //DO NOTHING
    }

    @Override
    protected void writeMetaToStorage() throws IOException {
        //合并到了索引文件中
        //DO NOTHING
    }

    @Override
    protected void readIndicesFromStorage() throws IOException {
        assert(indexFile.exists());//构造函数已经保证该文件是存在的
        indexFile.read(metaItem,pageMap);
    }

    @Override
    protected void writeIndicesToStorage() throws IOException {
        assert(indexFile.exists());//构造函数已经保证该表是存在的
        indexFile.drop();
        indexFile.create();
        indexFile.write(metaItem,pageMap);
    }


    @Override
    public void deleteByteArray(Identifier pageIdentifier) throws IOException {
        IndexItem e = this.pageMap.remove(pageIdentifier);
        if (e == null)
            throw new IOException("PaginationStorageManager.deleteByteArray: " + pageIdentifier.toString());
        //由于是AOF，删除的页面不会重复利用，因此这里不需要加入到EmptyPageIdentifiers中
        //for (int cIndex = 0; cIndex < e.getPageIdentifiers().size(); ++cIndex) {
        //    this.getEmptyPageIdentifiers().add((Identifier) e.getPageIdentifiers().get(cIndex).clone());
        //}
    }


    @Override
    public void storeByteArray(Identifier pageIdentifier, byte[] data) throws IOException {
        if (pageIdentifier.longValue() == StorageManager.NEW_PAGE) {
            IndexItem e = new IndexItem(data.length,new ArrayList<>());

            int ptr = 0;//从data数组的第0个字节开始
            Identifier cPage = Identifier.create(StorageManager.NEW_PAGE);
            int cRem = data.length;
            int cLen = 0;

            while (cRem > 0) {
                //AOF，直接添加到最后，不用处理删掉的空页面
                cPage.reset(this.getNextPageIdentifier().longValue());
                this.getNextPageIdentifier().increase();

                cLen = (cRem > this.getPageSize()) ? this.getPageSize() : cRem;
                System.arraycopy(data, ptr, this.pageBuffer, 0, cLen);

                writeBufferToPage(cPage);

                ptr += cLen;
                cRem -= cLen;
                e.getPageIdentifiers().add((Identifier) cPage.clone());
            }

            pageIdentifier.copyFrom(e.getPageIdentifiers().get(0));
            this.pageMap.put((Identifier) pageIdentifier.clone(), e);
        }
        else {
            // find the entry.
            IndexItem oldEntry = this.pageMap.remove(pageIdentifier);
            if (oldEntry == null)
                throw new IOException("PaginationStorageManager.storeByteArray: Invalid Page Exception");

            IndexItem e = new IndexItem(data.length,new ArrayList<>());

            int ptr = 0;
            Identifier cPage = Identifier.create(StorageManager.NEW_PAGE);
            int cRem = e.getDataLength();
            int cLen = 0;

            while (cRem > 0) {
                //AOF,这里直接添加到最后即可
                cPage.copyFrom(this.getNextPageIdentifier());
                this.getNextPageIdentifier().increase();

                cLen = (cRem > this.getPageSize()) ? this.getPageSize() : cRem;
                System.arraycopy(data, ptr, this.pageBuffer, 0, cLen);

                writeBufferToPage(cPage);

                ptr += cLen;
                cRem -= cLen;
                e.getPageIdentifiers().add((Identifier) cPage.clone());
            }
            this.pageMap.put((Identifier) pageIdentifier.clone(), e);
        }
    }

    /**
     * 在workDirectory下创建baseName目录,并返回全路径目录，系统将在baseName目录下建立索引文件和数据文件
     * @param workDirectory
     * @param baseName
     * @return
     * @throws IOException
     */
    public abstract boolean createDirectory(String workDirectory, String baseName) throws IOException;

    public abstract boolean existsFile(String fullPathName) throws IOException ;

    public abstract boolean existsDirectory(String fullPath) throws IOException;

    public abstract boolean existsDirectory(String workDirectory, String baseName) throws IOException;

    public abstract IndexFile createIndexFile() ;

    public abstract DataFiles createDataFiles() ;

}
